#include <kvs/glut/Application>
#include <kvs/glut/Screen>
#include <kvs/StructuredVolumeObject>
#include <kvs/StructuredVolumeImporter>
#include <kvs/PointObject>
#include <kvs/CellByCellMetropolisSampling>
#include <kvs/TransferFunction>
#include <kvs/ParticleBasedRenderer>
#include <kvs/Shader>
#include <kvs/Light>
#include <kvs/RotationMatrix33>
#include <string>
#include <iostream>
#include <kvs/PaintEventListener>
#include <kvs/RendererManager>


class FPS : public kvs::PaintEventListener
{
    void update()
    {
        std::cout << ((kvs::glut::Screen*)(screen()))->scene()->rendererManager()->renderer()->timer().msec() << std::endl;
    }
};

kvs::PointObject* ExtractParticles( const std::string& filename, const kvs::TransferFunction& tfunc, const size_t repetitions )
{
    kvs::StructuredVolumeObject* volume = new kvs::StructuredVolumeImporter( filename );
    volume->print( std::cout );

    const size_t subpixels = 1; // fixed to '1'
    const size_t level = subpixels * std::sqrt( double( repetitions ) );
    const float step = 0.5f;
    kvs::PointObject* object = new kvs::CellByCellMetropolisSampling( volume, level, step, tfunc );
    delete volume;
    return object;
}

int main( int argc, char** argv )
{
    kvs::glut::Application app( argc, argv );
    kvs::glut::Screen screen( &app );
    screen.setBackgroundColor( kvs::RGBColor::White() );
    screen.setSize( 800, 800 );
    screen.show();

    size_t repetitions = 81;

    kvs::ColorMap cmap1( 256 );
    cmap1.addPoint( 0, kvs::RGBColor::Yellow() );
    cmap1.addPoint( 255, kvs::RGBColor::Yellow() );
    cmap1.create();
    kvs::OpacityMap omap1( 256 );
    omap1.addPoint( 0, 0.0 );
//    omap1.addPoint( 110, 0 );
//    omap1.addPoint( 128, 1.0 );
//    omap1.addPoint( 140, 0 );
    omap1.addPoint( 255, 1.0 );
    omap1.create();
    kvs::TransferFunction tfunc1( cmap1, omap1 );

    kvs::ColorMap cmap2( 256 );
    cmap2.addPoint( 0, kvs::RGBColor( 255, 100, 100 ) );
    cmap2.addPoint( 255, kvs::RGBColor( 255, 100, 100 ) );
    cmap2.create();
    kvs::OpacityMap omap2( 256 );
    omap2.addPoint( 0, 0 );
    omap2.addPoint( 200, 0 );
    omap2.addPoint( 255, 1.0 );
    omap2.create();
    kvs::TransferFunction tfunc2( cmap2, omap2 );

    kvs::PointObject* object1 = ExtractParticles( argv[1], tfunc1, repetitions );
    object1->print( std::cout );

    kvs::PointObject* object = ExtractParticles( argv[2], tfunc2, repetitions );
    object->print( std::cout << std::endl );

    object->add( *object1 );
    object->print( std::cout << std::endl );
    delete object1;

    kvs::glsl::ParticleBasedRenderer* renderer = new kvs::glsl::ParticleBasedRenderer();
//    renderer->setShader( kvs::Shader::Phong() );
    renderer->setRepetitionLevel( repetitions );
    renderer->enableLODControl();
    screen.registerObject( object, renderer );

    kvs::Xform x = kvs::Xform::Scaling( 1.0 )
        * kvs::Xform::Rotation( kvs::YRotationMatrix33<float>( 55 ) )
        * kvs::Xform::Rotation( kvs::XRotationMatrix33<float>( 50 ) );
    object->multiplyXform( x );

    screen.scene()->camera()->setPosition( kvs::Vector3f( 0, 0, 6 ) );

    FPS fps;
    screen.addEvent( &fps );

    return app.run();
}
